
<template>
  <div class="vehicle-gps">
    <div class="map-warp">
      <div
        :id="mapRef"
        :ref="mapRef"
        :style="{
          height: mapHeight,
        }"

        class="map-content"
      />

      <div
        class="float-btn"
        :style="{
          bottom: backOriginIconPositionY
        }"
      >
        <div
          class="position-icon"
          @click="handleOpenPopup"
        >
          <svg-icon
            icon-class="list"
            class-name="position-icon--list"
          />
        </div>

        <div
          class="position-icon"
          @click="handleBackOrigin"
        >
          <svg-icon
            icon-class="position"
            class-name="position-icon__icon"
          />
        </div>
      </div>
    </div>

    <FloatingPanel
      :height="floatingPanelHeight"
      :anchors="anchors"
      @heightChangeEnd="handleFloatingPanelChangeEnd"
      @heightChange="handleFloatingPanelChange"
    >
      <div class="info-content">
        <VehicleInfo
          v-if="floatPanelShowType === 'VEHICLE_INFO'"
          :vehicle-info="currentVehicle"
          :auth-info="authInfo"
          @viewHistory="handleOpenDateTimePicker"
        />

        <StayPointInfo
          v-if="floatPanelShowType === 'STAY_POINT'"
          :stay-point="currentStayPoint"
          :time="formHistory.time"
          :mileage="mileage"
          @change="handleStayPointChange"
        />
      </div>
    </FloatingPanel>

    <FloatingPanel
      :height="listPanelHeight"
      :can-drag="false"
      @heightChangeEnd="handleCloseListPanel"
    > <div class="list-content">
      <div class="list-content__title">
        车辆信息
      </div>

      <div class="list-content__list">
        <VehicleList
          v-if="vehiclesList.length > 0"
          :vehicles="vehiclesList"
          :current-vehicle-id="currentVehicle?.id"
          :before-choice="handleBeforeChoice"
          @choice="handleVehicleListChoice"
        />
      </div>
    </div>
    </FloatingPanel>

    <VantPopup
      v-model="datePickerVisible"
      position="bottom"
      style="z-index: 99999;"
    >
      <VantDatetimePicker
        v-model="currentDateTime"
        type="date"
        :title="dateTimePickerTitle"
        :min-date="timePickerOptions.startDate"
        :max-date="timePickerOptions.endDate"
        @cancel="handleCloseDateTimePicker"
        @confirm="handleDateTimePickerConfirm"
      />
    </VantPopup>

    <VantOverlay
      :show="loading || isSwitchingVehicle"
      class="loading-overlay"
      :duration="0.3"
      z-index="10000"
    >
      <div class="loading-wrapper">
        <VantLoading type="spinner" color="#1989fa" size="40px" />
        <div class="loading-text">车辆信息加载中...</div>
        <div v-if="isSwitchingVehicle" class="loading-text">正在切换车辆...</div>
      </div>
    </VantOverlay>
  </div>
</template>

<script>
import TdtMixin from '@/mixins/TdtMixin'
import vehicleGpsMixin from '@/views/vehicleService/js/vehicleGpsMixin'
import '@/assets/Tdt/D3SvgOverlay'
import '@/assets/Tdt/CarTrack'
import FloatingPanel from '@/components/FloatingPanel/FloatingPanel.vue'
import VehicleInfo from '@/views/commonPage/components/vehicleGps/VehicleInfo.vue'
import VehicleList from '@/views/commonPage/components/vehicleGps/VehicleList.vue'
import {
  DatetimePicker as VantDatetimePicker,
  Popup as VantPopup,
  Toast as VantToast,
  Loading as VantLoading,
  Overlay as VantOverlay
} from 'vant'
import 'vant/lib/datetime-picker/style'
import 'vant/lib/popup/style'
import 'vant/lib/toast/style'
import 'vant/lib/loading/style'
import 'vant/lib/overlay/style'
import dayjs from 'dayjs'
import { sleep } from '@/utils'
import StayPointInfo from '@/views/commonPage/components/vehicleGps/StayPointInfo.vue'

const throttle = (fn, delay = 600) => {
  let lastExec = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastExec >= delay) {
      lastExec = now
      fn.apply(this, args)
    } else {
      console.warn('点击太快，忽略切换')
    }
  }
}

const VEHICLE_INFO = 'VEHICLE_INFO'
const STAY_POINT = 'STAY_POINT'

function wrapSetCarMarker(vm, e, currentRequestId) {
  return new Promise((resolve) => {
    (async() => {
      try {
        await vm.setCarMarker(e, currentRequestId)
      } catch (error) {
        console.error('setCarMarker error:', error)
      } finally {
        await sleep(200)
        resolve()
      }
    })()
  })
}
// ----------------------------------------------------

export default {
  name: 'VehicleGps',
  components: { StayPointInfo, VehicleList, VehicleInfo, FloatingPanel, VantDatetimePicker, VantPopup, VantLoading, VantOverlay },
  mixins: [TdtMixin, vehicleGpsMixin],
  data() {
    return {
      floatingPanelHeight: 0,
      mapHeight: '100%',
      listPanelHeight: 0,
      datePickerVisible: false,
      currentDateTime: '',
      formHistory: {
        time: [],
        id: null
      },
      dateTimePickerTitle: '选择开始时间',
      floatPanelShowType: VEHICLE_INFO,
      animationFrameId: null,
      animating: false,
      loading: false, // 控制加载动画和 UI 锁定
      isSwitchingVehicle: false, // 控制切换车辆时的状态锁定
      lastGpsRequest: {}, // 车辆 GPS 缓存 { id: timestamp }
      gpsCacheTimeout: 5000,
      throttledVehicleChoice: null,
      throttledChangeMapHeight: null,
      requestId: 0,
      currentVehicle: null, // 默认应为 null
      authInfo: {},
      vehiclesList: [], // 假设车辆列表数据在这里，初始化为空
      stayPositionList: [],
      currentStayPoint: null,
      mileage: 0
    }
  },
  computed: {
    anchors() {
      return [100, window.innerHeight * 0.4, window.innerHeight * 0.6]
    },

    backOriginIconPositionY() {
      return this.floatingPanelHeight > 0
        ? this.floatingPanelHeight + 10 + 'px' : '20px'
    },

    getCurrentVehicleLonLat() {
      let lon, lat
      if (this.floatPanelShowType === VEHICLE_INFO) {
        lon = this.currentVehicle?.lastGps?.lastLon
        lat = this.currentVehicle?.lastGps?.lastLat
      } else if (this.floatPanelShowType === STAY_POINT) {
        lon = this.currentStayPoint?.lon
        lat = this.currentStayPoint?.lat
      }
      return {
        lon,
        lat
      }
    },

    timePickerOptions() {
      const startDate = new Date(dayjs().year(), 0, 1)
      const endDate = new Date(dayjs().year(), dayjs().month(), dayjs().subtract(1, 'day').date())
      return {
        startDate,
        endDate
      }
    }
  },
  watch: {
    vehicleMarkerClick(nVal) {
      if (nVal && !this.loading && !this.isSwitchingVehicle) {
        this.loading = true
        this.floatPanelShowType = VEHICLE_INFO
        this.handleOpenFloatPanel()
        this.$nextTick(() => this.changeMapHeight(false))
        this.vehicleMarkerClick = false
        setTimeout(() => { this.loading = false }, 1000)
      }
    },

    stayMarkerClick(nVal) {
      if (nVal && !this.loading && !this.isSwitchingVehicle) {
        this.loading = true
        this.floatPanelShowType = STAY_POINT
        this.handleOpenFloatPanel()
        this.$nextTick(() => this.changeMapHeight(true))
        this.stayMarkerClick = false
        setTimeout(() => { this.loading = false }, 1000)
      }
    }
  },
  mounted() {
    document.title = '车辆位置'
    this.$nextTick(() => {
      this.throttledChangeMapHeight = throttle(this.changeMapHeight.bind(this), 300)
      this.throttledVehicleChoice = throttle(this.handleVehicleListChoiceInternal.bind(this), 600)

      // 1. 初始化地图 (Mixin中的 init) - 仅初始化地图实例和默认中心点
      this.init()

      // 2. 在地图初始化完成后，开始获取车辆数据并初始化
      setTimeout(() => {
        this.addGlobalEvent()
        this.initializeVehicleData()
      }, 500)
    })
  },
  beforeDestroy() {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
    }
    if (this.map && this.mapClickHandler) {
      this.map.removeEventListener('click', this.mapClickHandler)
    }
    if (this.gpsInterval) clearInterval(this.gpsInterval)
  },
  methods: {
    async initializeVehicleData() {
      this.loading = true

      if (typeof this.getVehiclesList === 'function') {
        try {
          this.vehiclesList = await this.getVehiclesList()
        } catch (e) {
          console.error('获取车辆列表失败:', e)
        }
      }

      if (!this.vehiclesList || this.vehiclesList.length === 0) {
        console.warn('初始化成功，但暂无车辆信息可显示。')
        this.loading = false
        return
      }

      this.currentVehicle = this.getCurrentVehicle(this.vehiclesList)

      const allMarkerPromises = this.vehiclesList
        .filter(v => v?.imei)
        .map(vehicle => this.setCarMarker(vehicle))

      await Promise.allSettled(allMarkerPromises)

      if (this.currentVehicle) {
        await this.handleVehicleListChoiceInternal(this.currentVehicle)
      }

      this.loading = false
    },

    handleBackOrigin() {
      if (!this.currentVehicle) return
      const { lon, lat } = this.getCurrentVehicleLonLat
      if (lon && lat) {
        this.setCenterAndZoom(lon, lat, 18)
      }
    },

    handleFloatingPanelChange(e) {
      this.floatingPanelHeight = e
    },

    handleFloatingPanelChangeEnd(e) {
      this.floatingPanelHeight = e
      this.throttledChangeMapHeight(true)
    },

    changeMapHeight(needSetCenter = true, zoom) {
      const height = this.floatingPanelHeight
      setTimeout(() => {
        this.mapHeight = `calc(100% - ${height - 20}px)`
        this.checkResize()
        const { lon, lat } = this.getCurrentVehicleLonLat
        const zoomLevel = zoom || this.getMapZoom()
        if (needSetCenter && lon && lat) {
          this.setCenterAndZoom(lon, lat, zoomLevel)
        }
      }, 300)
    },

    handleOpenFloatPanel() {
      this.floatingPanelHeight = this.anchors[1]
    },

    handleCloseFloatPanel() {
      this.floatingPanelHeight = 0
      this.changeMapHeight(false)
    },

    handleOpenPopup() {
      this.listPanelHeight = window.innerHeight * 0.8
    },

    handleCloseListPanel() {
      this.listPanelHeight = 0
    },

    handleBeforeChoice(e) {
      if (!e.imei) {
        VantToast('该车辆暂未绑定GPS设备，请稍后重试')
        return false
      }
      return true
    },

    /**
* @description 内部处理车辆切换逻辑，包含节流和缓存判断。
* @param {Object} e 车辆信息
*/
    async handleVehicleListChoiceInternal(e) {
      if (this.loading || this.isSwitchingVehicle) {
        console.warn('切换中，忽略请求')
        return
      }

      if (!e || !e.id) {
        console.error('车辆信息不完整，无法切换。')
        return
      }

      const now = Date.now()
      const lastRequestTime = this.lastGpsRequest[e.id]
      if (lastRequestTime && (now - lastRequestTime < this.gpsCacheTimeout)) {
        console.warn(`车辆 ${e.id} 最近请求过，忽略 GPS`)
        this.updateUIWithoutRequest(e)
        return
      }

      this.isSwitchingVehicle = true
      this.loading = true
      this.requestId++
      const currentRequestId = this.requestId
      this.lastGpsRequest[e.id] = now

      if (this.gpsInterval) {
        clearInterval(this.gpsInterval)
        this.gpsInterval = null
        console.log('清除了旧 GPS 定时器')
      }

      // 仅清除轨迹和自定义标记，保留其他车辆标记 (markers)
      this.clearTrack()
      this.clearCustomMarker() // 清除当前车辆标记，等待 wrapSetCarMarker 重新添加
      this.floatPanelShowType = VEHICLE_INFO

      this.currentVehicle = e
      this.handleCloseListPanel()
      this.handleOpenFloatPanel()

      try {
        await wrapSetCarMarker(this, e, currentRequestId)

        // 强制触发 VehicleInfo 的 watch
        this.currentVehicle = { ...this.currentVehicle }

        // GPS 数据获取完毕后，再进行地图居中
        this.throttledChangeMapHeight(true, 18)

        if (this.requestId !== currentRequestId) {
          console.warn(`请求ID ${currentRequestId} 已过期，不解除锁定。`)
          return
        }
      } catch (error) {
        console.error('车辆 GPS 切换失败:', error)
      } finally {
        this.isSwitchingVehicle = false
        this.loading = false
        console.log(`车辆切换请求ID: ${currentRequestId} 锁定已解除。`)
      }
    },

    updateUIWithoutRequest(e) {
      this.clearTrack()
      this.clearCustomMarker()
      this.floatPanelShowType = VEHICLE_INFO
      this.$nextTick(() => {
        this.currentVehicle = e
        this.handleCloseListPanel()
        this.handleOpenFloatPanel()
        // 缓存情况：GPS 数据已存在，可以立即调整
        this.throttledChangeMapHeight(true, 18)
      })
    },

    handleVehicleListChoice(e) {
      this.throttledVehicleChoice(e)
    },

    handleOpenDateTimePicker() {
      if (!this.currentVehicle || !this.currentVehicle.id) {
        VantToast('请先选择一辆车辆')
        return
      }
      this.formHistory.time = []
      const date = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
      this.currentDateTime = new Date(date)
      this.datePickerVisible = true
    },

    handleCloseDateTimePicker() {
      this.datePickerVisible = false
    },

    async handleDateTimePickerConfirm(e) {
      this.requestId++
      const currentRequestId = this.requestId

      if (this.formHistory.time.length <= 0) {
        this.formHistory.time[0] = dayjs(e).format('YYYY-MM-DD')
        this.dateTimePickerTitle = '选择结束时间'
        VantToast('继续选择结束时间')
      } else {
        if (dayjs(e).isBefore(this.formHistory.time[0])) {
          VantToast('结束时间不能早于开始时间')
          return
        }
        this.formHistory.time[1] = dayjs(e).format('YYYY-MM-DD')
        this.handleCloseDateTimePicker()
        await sleep(500)
        VantToast.clear()
        this.dateTimePickerTitle = '选择开始时间'

        const [startTime, endTime] = this.formHistory.time
        const start = dayjs(startTime).format('YYYY-MM-DD HH:mm:ss')
        const end = dayjs(endTime).endOf('day').format('YYYY-MM-DD HH:mm:ss')
        const params = {
          start,
          end,
          id: this.currentVehicle.id
        }
        console.log(`轨迹查询ID: ${this.requestId}`)

        await this.queryHistory(params, currentRequestId)

        this.handleCloseFloatPanel()
      }
    },

    addGlobalEvent() {
      const throttledClick = throttle((e) => {
        const target = e.originalEvent.target
        if (target.nodeName === 'IMG' || this.floatingPanelHeight === 0) {
          return
        }
        this.floatingPanelHeight = this.anchors[0]
        this.changeMapHeight()
      }, 200)

      this.map.addEventListener('click', throttledClick)
      this.mapClickHandler = throttledClick
    },

    handleStayPointChange(type) {
      if (this.currentStayPoint?.marker) {
        this.cancelMarkerFlashing(this.currentStayPoint.marker)
      }

      console.log('type --------->', type)
      const len = this.stayPositionList.length
      let currentIndex = this.currentStayPoint?.index ?? 0
      const typeMap = {
        prev: -1,
        next: 1
      }

      currentIndex = typeMap[type] + currentIndex
      if (currentIndex < 0) {
        currentIndex = len - 1
      } else if (currentIndex >= len) {
        currentIndex = 0
      }

      this.currentStayPoint = this.stayPositionList[currentIndex]
      if (this.currentStayPoint) {
        const { lon, lat } = this.getCurrentVehicleLonLat
        this.setCenterAndZoom(lon, lat, this.getMapZoom())

        if (this.currentStayPoint?.marker) {
          this.startMarkerFlashing(this.currentStayPoint.marker)
        }
      }
    },

    startMarkerFlashing(marker) {
      let start = null
      const duration = 1000
      this.animating = true

      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId)
        this.animationFrameId = null
      }

      const animate = (timestamp) => {
        if (!this.animating) {
          marker.setOpacity(1)
          return
        }
        if (!start) start = timestamp
        const elapsed = (timestamp - start) % duration
        const progress = elapsed / duration

        const opacity = (Math.sin(progress * Math.PI * 2) + 1) / 2 * 0.5 + 0.5

        marker.setOpacity(opacity)

        this.animationFrameId = requestAnimationFrame(animate)
      }

      this.animationFrameId = requestAnimationFrame(animate)
    },

    cancelMarkerFlashing(marker) {
      this.animating = false
      if (this.animationFrameId) {
        cancelAnimationFrame(this.animationFrameId)
      }
      this.animationFrameId = null
      if (marker) marker.setOpacity(1)
    }
  }
}
</script>

<style>
.tdt-control-copyright {
 display: none !important;
}
.tdt-control-datasources {
 display: none !important;
}
</style>

<style lang="scss" scoped>
.vehicle-gps {
 height: 100vh;
 width: 100%;
 position: relative;
 background-color: #DDDDDD;

 .map-warp {
 width: 100%;
 height: 100%;
 will-change: width;
 position: relative;

 .map-content {
 width: 100%;
 height: 100%;
 position: relative;
 }

 .float-btn {
 position: absolute;
 right: 10px;
 z-index: 9999;
 transition: all .3s;

 .position-icon {
 width: 40px;
 height: 40px;
 border-radius: 50%;
 display: flex;
 justify-content: center;
 align-items: center;
 background-color: #fff;
 box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 margin-bottom: 10px;

 &:last-child {
 margin-bottom: 0;
 }

 &__icon {
 width: 20px;
 height: 20px;
 }

 &--list {
 width: 16px;
 height: 16px;
 }
 }
 }

 }

 .info-content {
 width: 100%;
 height: 100%;
 padding: 0 12px 12px 12px;
 }

 .list-content {
 background-color: #F5F5F5;
 width: 100%;
 height: 100%;

 &__title {
 display: flex;
 justify-content: center;
 align-items: center;
 padding: 12px;
 background-color: #fff;
 text-align: center;
 font-weight: bold;
 }

 &__list {
 padding: 12px;
 height: calc(100% - 45px);
 overflow-y: auto;
 }
 }
}

::v-deep .rotated-marker {
 z-index: 999;
 pointer-events: auto;
}

::v-deep .rotated-marker img {
 width: 100%;
 height: 100%;
 display: block;
}

// 加载动画样式
.loading-overlay {
 display: flex;
 align-items: center;
 justify-content: center;
 background-color: rgba(255, 255, 255, 0.8);

 .loading-wrapper {
 display: flex;
 flex-direction: column;
 align-items: center;
 padding: 20px;
 border-radius: 8px;
 background-color: #fff;
 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 }

 .loading-text {
 margin-top: 10px;
 color: #666;
 font-size: 14px;
 }
}
</style>
